package com.xja.shoppingmall.comfig;

import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.common.utils.CollectionUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.Executor;

/**
 * @author CHENCHEN
 * @title   通过Nacos 下发动态路由配置，监听Nacos中路由配置变更
 * @createTime 2021-12-1111:36
 */
@Slf4j
@Component
@DependsOn({"gatewayConfig"}) //@DependsOn在某个bean实例化后在执行
public class DynamicRouteServiceImplByNacos {

    //Nacos配置服务
    private ConfigService configService;

    @Autowired
    private DynamicRouteServiceImpl dynamicRouteService;

    /**
     * Bean 在容器中构造完之后会执行 init 方法
     */
    @PostConstruct
    public void init(){
        log.info("gateway route init ...");
        try{
            //初始化nacos配置客户端
            configService = initConfigService();
             if(configService == null){
                 log.error("gateway init config fail");
                 return;
             }
             //通过 Nacos Config 并指定路由配置路径去获取路由配置
            String configInfo = configService.getConfig(
                    GatewayConfig.NACOS_ROUTE_DATA_ID,
                    GatewayConfig.NACOS_ROUTE_GROUP,
                    GatewayConfig.DEFAULT_TIMEOUT
            );
             log.info("get current gateway configInfo: [{}]", configInfo);
            List<RouteDefinition> routeDefinitions = JSON.parseArray(configInfo, RouteDefinition.class);
            if(CollectionUtils.isNotEmpty(routeDefinitions)){
                routeDefinitions.forEach(definition -> {
                    log.info("init gateway config: [{}]", definition);
                    dynamicRouteService.addRouteDefinition(definition);
                });
            }
        }catch (Exception e){
            log.error("gateway route init has some error: [{}]", e.getMessage(), e);
        }
        //设置监听器 监听ID和分组变更
        dynamicRouteByNacosListener(GatewayConfig.NACOS_ROUTE_DATA_ID, GatewayConfig.NACOS_ROUTE_GROUP);
    }

    /**
     * 初始化nacos配置信息
     * @return
     */
    private ConfigService initConfigService(){
        try{
            Properties properties = new Properties();
            properties.setProperty("serverAddr", GatewayConfig.NCOS_SERVER_ADDR);
            properties.setProperty("namespace",GatewayConfig.NACOS_NAMESPACE);
            return configService = NacosFactory.createConfigService(properties);
        }catch (Exception e){
            log.error("gateway init nacos config error : [{}]", e.getMessage(), e);
            return null;
        }
    }

    /**
     * 监听Nacos下发动态路由配置
     * @param dataId
     * @param group
     */
    private void dynamicRouteByNacosListener(String dataId, String group){
        try{
            configService.addListener(dataId, group, new Listener() {
                /**
                 * 自己提供线程池操作
                 * @return
                 */
                @Override
                public Executor getExecutor() {
                    return null;
                }

                /**
                 * 监听收到配置更新
                 * @param s
                 */
                @Override
                public void receiveConfigInfo(String s) {
                    log.info("get config to update config: [{}]", s);
                    List<RouteDefinition> definitions = JSON.parseArray(s, RouteDefinition.class);
                    log.info("update route data: [{}]", definitions);
                    dynamicRouteService.updateList(definitions);
                }
            });
        }catch (NacosException e){
            log.error("dynamic update gateway config error: [{}]",e.getMessage(), e);
        }
    }
}
